package cmp.compile;
/*****************************************************
 * Creates Overview (summary) that is display on viewMulti
 */
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.sql.ResultSet;

import java.util.ArrayList;
import java.text.DecimalFormat; 

import cmp.main.Globals;
import assem.DBConn;
import cmp.database.DBinfo;
import cmp.compile.panels.CompilePanel;
import util.Debug.ErrorReport;
import util.methods.TimeHelpers;

public class Summary {	
    public Summary (int type, DBConn dbc, CompilePanel panel) 
                throws Exception
    {
		info = new DBinfo(dbc, true); // check version and update
		
		 if (type>0) CompileMain.PrtSpMsg(0, "\nSummary");
         cmpDBC = dbc; 
         cmpPanel = panel;
         String text="";
         
         lines.add("multiTCW " + Globals.VERSION);
         makeDatasets();
         makeExplevel();
         makeDE();
         if (type!=1) {
        	 	makeMethods();
        	 	makePOG();
         }   	 
 		 for (int i=0; i< lines.size(); i++)
 			text += lines.get(i) + "\n";
 		 cmpDBC.executeUpdate("UPDATE info SET summary = \"" + text + "\""); 
     	 writeOverview(text);
     	 
         if (type==1) System.out.println(text);
         else if (type==2) { // only print methods
        	 	lines.clear(); text="";
        	 	makeSizeTable();
        	 	for (int i=0; i< lines.size(); i++) text += lines.get(i) + "\n";
        	 	System.out.println(text);
         } 
    }   
 
    /**************************************************************
     * Information about Species in compare database
     */
  
    private void makeDatasets() {
    		ResultSet rs;
    		try {
    			String date = "";
				
    			rs = cmpDBC.executeQuery("SELECT compiledate FROM info");
    			if (rs.next()) 
    				date = TimeHelpers.convertDate(rs.getString(1));
    			else 
    				CompileMain.die("Fatal error reading info from database");
    			   			
    			nAsm = cmpDBC.executeCount("SELECT COUNT(*) FROM assembly");
    			
    			lines.add("");
    			lines.add("DATASETS: " + nAsm + "  Created " + date);
    			
    		    String[] fields = {"Species", "#aaSeq", "#ntSeq", "#annoSeq",  
    		    		"#EXP", "#annoDB",  "Created", "Remark"};
    	        int [] just = {1,0,0, 0,0,0,1,1};
    	        int nCol=fields.length;
    			String [][] rows = new String[nAsm+1][nCol];
    	
    	        rs = cmpDBC.executeQuery("SELECT ASMid, ASMstr,  nPep, nUT, nAnnoUT, nExp,  " +
    	        		"nAnnoDB, assemblydate, remark, isPep from assembly");
    	   		int r=0, cntAA=0, cntNT=0, cntAN=0;
    	   		while(rs.next()) {
    	   			assmID.add(rs.getInt(1));
    	   			String asmstr = rs.getString(2);
    	   			assmStr.add(asmstr);
    	   			int isPep = rs.getInt(10);
    	   			
    	   			cntAN += rs.getInt(5);
    	   			
    	   			rows[r][0] = asmstr;
    	   			if (isPep==1) {
    	   				rows[r][1] = df.format(rs.getInt(4)); // aaSeq
    	   				rows[r][2] = df.format(0); // ntSeq
    	   				cntAA += rs.getInt(4);
    	   			}
    	   			else {
    	   				rows[r][1] = df.format(rs.getInt(3)); // aaSeq
    	   				rows[r][2] = df.format(rs.getInt(4)); // ntSeq
    	   				cntAA += rs.getInt(3);
    		   			cntNT += rs.getInt(4);
    	   			}
    	   			rows[r][3] = df.format(rs.getInt(5)); // annoSeq
    	   			rows[r][4] = df.format(rs.getInt(6)); // #EXP
    	   			rows[r][5] = df.format(rs.getInt(7)); // #annoDB
    	   			rows[r][6] = TimeHelpers.convertDate(rs.getString(8));
    	   			rows[r][7] = rs.getString(9); // remark
    	   			r++;
            } 
    	   		rows[r][0] = "TOTAL";
    	   		rows[r][1] = df.format(cntAA);
    	   		rows[r][2] = df.format(cntNT);
    	   		rows[r][3] = df.format(cntAN);
    	   		rs.close();
    	   		makeTable(nCol, r+1, fields, just, rows);
    	   		lines.add("");
    		}
    		catch (Exception e) {
    			ErrorReport.die(e, "create dataset Table for summary");
    		}
    }
    private void makeExplevel() {
		ResultSet rs;
		try {
			String [] libCol = info.getSeqLib();
			int nLib = libCol.length;
			// CAS 5/24/15 not sure what this meant
			//if (info.isPeptide()) lines.add("Count libraries  ");
			//else lines.add("Expression libraries (raw counts; AA sequences only): ");
			lines.add("Count libraries  ");
			
			// make Column names for table; indexes are 1 to nLib (0 is assembly name)
			int nCol = nLib+1;
			String [][] rows = 		new String[nAsm][nCol];
			int [][] expCnt = 		new int[nAsm][nCol];
		    String[] libFields = 	new String[nCol];
		    int [] libJust = 		new int[nCol];
		    
	    		libFields[0] = "Species";
	    		libJust[0] = 1;
		    for (int i=0; i< nLib; i++) {
		    		libFields[i+1] = libCol[i];
		    		libJust[i+1] = 0;
		    }

			for (int i=0; i< nAsm; i++)
				for (int j=0; j < nCol; j++) {
					rows[i][j] = "--";
					expCnt[i][j]=0;
				}
	
			// for each assembly, sum values in expList (using unnormalized)
	        for (int r=0; r< assmID.size(); r++) 
	        {
		        	rs = cmpDBC.executeQuery("SELECT  expList FROM unitrans " +
		        			"WHERE ASMid = " + assmID.get(r) + " and aaLen>0");
		        	// loop through all unitrans
		        	while (rs.next()) {
		        		String libCounts = rs.getString(1);
		    			String [] groups = libCounts.split("\\s+");
					for(int i=0; i<groups.length; i++) {
						String [] pair = groups[i].split("=");
						if (pair.length==2) { 
							int c=-1;
							for (int j=0; j<nLib && c== -1; j++) 
								if (libCol[j].equals(pair[0])) c = j;
	       	 				int v = Integer.parseInt(pair[1]);
	       	 				if (v>0) expCnt[r][c+1] += v;
						}
	       	 		}
		        	} 
		     	rs.close();
	        }
	   		
	   		// create table
	        for (int r=0; r< assmID.size(); r++) 
	        {
		        	rows[r][0] = assmStr.get(r);
		        	for (int c=1; c < nCol; c++) {
		        		if (expCnt[r][c] > 0) {
		        			int x=expCnt[r][c];
		        			String k = "";
		        			if (x > 9999) {
		        				x = expCnt[r][c]/1000;
		        				k = "k";
		        			}
		        			rows[r][c] = df.format(x) + k;
		        		}
		        	}
	        }
	        makeTable(nCol, nAsm, libFields, libJust, rows);
	        lines.add("");
		}
		catch (Exception e) {
			ErrorReport.die(e, "create dataset Table for summary");
		}
    }
    private void makeDE() {
		ResultSet rs;
		try {
			String [] deCol = info.getSeqDE();
			int nDE = deCol.length;
			if (nDE == 0) return;
			// CAS 5/24/15 not sure what this meant
			//if (info.isPeptide()) lines.add("Differential Expression (count of < " + Globals.DEcutoff + "): ");
			//else lines.add("Differential Expression (count of < " + Globals.DEcutoff + "; AA sequences only): ");
			lines.add("Differential Expression (count of < " + Globals.DEcutoff + "): ");
			
			// make Column names for table; indexes are 1 to nLib (0 is assembly name)
			int nCol = nDE+1;
			String [][] rows = 		new String[nAsm][nCol];
			int [][] deCnt = 		new int[nAsm][nCol];
		    String[] deFields = 		new String[nCol];
		    int [] deJust = 			new int[nCol];
		    int [][] allCnt =			new int[nAsm][nCol];
		    
	    		deFields[0] = "Species";
	    		deJust[0] = 1;
		    for (int i=0; i< nDE; i++) {
		    		deFields[i+1] = deCol[i];
		    		deJust[i+1] = 0;
		    }

			for (int i=0; i< nAsm; i++)
				for (int j=0; j < nCol; j++) {
					rows[i][j] = "--";
					deCnt[i][j]=0;
					allCnt[i][j]=0;
				}
			String deSQL = info.getSeqDESQL(); // same order as columns
			// for each assembly, sum values in expList (using unnormalized)
	        for (int r=0; r< assmID.size(); r++) 
	        {
		        	rs = cmpDBC.executeQuery("SELECT " + deSQL + " FROM unitrans " +
		        			"WHERE ASMid = " + assmID.get(r) + " and aaLen>0");
		        	// loop through all unitrans
		        	while (rs.next()) {
					for(int i=1; i<=deCol.length; i++) {
						double d = rs.getDouble(i);
						if (d != -1) {
							if (Math.abs(d) < Globals.DEcutoff) deCnt[r][i]++;
							allCnt[r][i]++;
						}
	       	 		}
		        	} 
		     	rs.close();
	        }
	   		
	   		// create table
	        for (int r=0; r< assmID.size(); r++) 
	        {
		        	rows[r][0] = assmStr.get(r);
		        	for (int c=1; c < nCol; c++) {
		        		if (allCnt[r][c] > 0) { // need to distinguish no DE for this column vs no <0.5 DE
		        			int x=deCnt[r][c];
		        			String k = "";
		        			if (x > 9999) {
		        				x = deCnt[r][c]/1000;
		        				k = "k";
		        			}
		        			if (x==0) rows[r][c]="0";
		        			else rows[r][c] = df.format(x) + k;
		        		}
		        	}
	        }
	        makeTable(nCol, nAsm, deFields, deJust, rows);
	        lines.add("");
		}
		catch (Exception e) {
			ErrorReport.die(e, "create dataset Table for summary");
		}
    }
    /********************************************************
     * Table of Methods in compare database
     */
    private void makeMethods() 
    {
   		ResultSet rs;
		try { 			      
	        nMethod = cmpDBC.executeCount("SELECT COUNT(*) FROM pog_method");
			lines.add("METHODS: " + nMethod);
	        if (nMethod==0) return;
	        	
	        String[] fields = {"Prefix", "Name", "Added", "Remark"};
	        int [] just = {1,1,1, 1};
	        int nCol=fields.length;
			String [][] rows = new String[nMethod][nCol];
	
	        String strQ = "SELECT PMid, PMstr, prefix, adddate, description from pog_method";
	   		rs = cmpDBC.executeQuery(strQ);
	   		int r=0;
	   		while(rs.next()) {
	   			rows[r][0] = rs.getString(3);
	   			rows[r][1] = rs.getString(2);
                rows[r][2] = TimeHelpers.convertDate(rs.getString(4));
                rows[r][3] =  rs.getString(5);
	            r++;
	            methods.add(rs.getString(1) + " " + rs.getString(3));
            } 
	   		rs.close();	   		
	   		makeTable(nCol, r, fields, just, rows);
	   		lines.add("");
		}
		catch (Exception e) {
			ErrorReport.die(e, "create Method Table for summary");
		}
    }
    /************************************************************************
     * Information about Groups
     *******************************************************************/
    private void makePOG()
    {
		try {
			if (nMethod==0) return;
			int cnt = cmpDBC.executeCount("SELECT count(*) FROM pog_groups ");
    	 		lines.add("Clusters: " + df.format(cnt));
			makeSizeTable();
			lines.add("");
			makeTaxaTable();
		}
	    catch (Exception err) {
			ErrorReport.prtReport(err, "reading database for Clusters");
			return;
	    }
    }
    
    private void makeSizeTable() 
	{	
	    String [] dfields = {"Method", "=2", "3-5", "6-10", "11-20", "21-30", "31-40", "41-50", ">50", "Total", "#Seqs"};
	    int [] djust = 	    {1,         0,     0,     0,       0,      0,        0,        0,     0,    0,  0};
	    int[] start=                   {1,     2,     5,      10,      20,      30,       40,     50};
	    int[] end=                     {2,     5,     10,     20,      30,      40,       50,    100000000};

	    int nSiz = start.length;
	    int nCol=dfields.length;
	    String [][] rows = new String[nMethod][nCol+1];
	    lines.add("   Sizes: ");
	    
	    try {
		    	for (int r = 0; r < methods.size(); r++) {
		    		String [] s = methods.get(r).split(" ");
		    		rows[r][0] = s[1];
		    		int total=0;
		    		for (int i = 0; i<nSiz; i++) {
		    			int cnt =  cmpDBC.executeCount("SELECT count(*) FROM pog_groups " +
			   	          " WHERE count > " + start[i] + " AND count <= " + end[i] + 
			   	          " AND PMid = " + s[0]);
		    			rows[r][i+1] = df.format(cnt);
		    			total += cnt;
		    		}
		    		rows[r][nCol-2] = df.format(total);
		    		total = cmpDBC.executeCount("SELECT SUM(count) from pog_groups " +
		    				" WHERE PMid = " + s[0]);
		    		rows[r][nCol-1] = df.format(total);
		    		if (total==0) {
		    		    System.err.println("+++ No clusters found for " + rows[r][0]);  		    
		    		}
		    }
		    makeTable(nCol, nMethod, dfields, djust, rows);
	    }
	    catch (Exception err) {
			ErrorReport.prtReport(err, "reading database for Group count");
			return;
	    }	
	}

    private void makeTaxaTable() {
    try {
	    lines.add("   Taxa categories: ");
	    String [] dfields = new String [(nAsm * 2) + 1 ];
	    int [] djust = 	    new int [(nAsm * 2) + 1];
	    dfields[0] = "Method";
	    djust[0] = 0;
	    
	    int c=1;
	    for (int i=2; i<= nAsm; i++) dfields[c++] = i + "x1";
	    for (int i=1; i<= nAsm; i++) dfields[c++] = i + "xN"; 
	    for (int i=0; i<c; i++) djust[i]=1;
	    
	    int nCol=djust.length;
	    String [][] rows = new String[nMethod+1][nCol+1];
	      
    		int cnt=0;
    		String taxa;
    		
	    	for (int r = 0; r < methods.size(); r++) {
	    		c=0;
	    		String [] s = methods.get(r).split(" ");
	    		rows[r][c++] = s[1];
	   
	    		for (int i = 2; i <= nAsm; i++) {
	    			taxa = i + "x1";
	    			cnt =  cmpDBC.executeCount("SELECT count(*) FROM pog_groups " +
	    					"WHERE taxa = '" + taxa + "' AND PMid=" + s[0]);
	    			rows[r][c++] = df.format(cnt);
	    		}
	    		for (int i = 1; i <= nAsm; i++) {
	    			taxa = i + "xN";
	    			cnt =  cmpDBC.executeCount("SELECT count(*) FROM pog_groups " +
	    					"WHERE taxa = '" + taxa + "' AND PMid=" + s[0]);
	    			rows[r][c++] = df.format(cnt);
	    		}
	    }
	    makeTable(nCol, nMethod, dfields, djust, rows);
    }
    catch (Exception e) {ErrorReport.prtReport(e, "reading database for Taxa");}	
    }
    ////////////////////////////////////////////////////////////////////////
    // XXX 
	private void makeTable(int nCol, int nRow, String[] fields, int [] justify, String [][] rows)
	{
		int c, r;
		String line;
		String space = "  ";
		
		// compute column lengths
		int []collen = new int [nCol];
		for (c=0; c < nCol; c++) collen[c] = 0;
		
        for (c=0; c< nCol; c++) { // longest value
            for (r=0; r<nRow && rows[r][c] != null; r++) {
        		if (rows[r][c] == null) rows[r][c] = "";
        		if (rows[r][c].length() > collen[c]) 
        			collen[c] = rows[r][c].length();
            }
        }
        if (fields != null) {    // heading longer than any value?
			for (c=0; c < nCol; c++) {
				if (collen[c] > 0) {
					if (fields[c] == null) fields[c] = "";
					if (fields[c].length() > collen[c]) 
						collen[c]=fields[c].length();
				}
			}
	        // output headings
	        line = "      ";
	        for (c=0; c< nCol; c++) 
	        		if (collen[c] > 0) 
	        			line += pad(fields[c],collen[c], justify[c]) + space;
	        lines.add(line);
        }
        // output rows
        for (r=0; r<nRow; r++) {
        		line = "      ";
            for (c=0; c<nCol; c++) {
                 if (collen[c] > 0) 
                	 	line += pad(rows[r][c],collen[c],justify[c]) + space;
                 rows[r][c] = ""; // so wouldn't reuse in next table
            }
            lines.add(line);
        }
	}
	
    private static String pad(String s, int width, int o)
    {
		if (s == null) return " ";
        if (s.length() > width) {
            String t = s.substring(0, width-1);
            System.out.println("'" + s + "' truncated to '" + t + "'");
            s = t;
            s += " ";
        }
        else if (o == 0) { // left
            String t="";
            width -= s.length();
            while (width-- > 0) t += " ";
            s = t + s;
        }
        else {
            width -= s.length();
            while (width-- > 0) s += " ";
        }
        return s;
    }
    
	public void writeOverview(String text) {
		String overFilePath = cmpPanel.getCurrentProjectDirectory() + "/" + Globals.Compile.overFile;
		try {
			FileOutputStream out = new FileOutputStream(overFilePath);
			PrintWriter fileObj = new PrintWriter(out); 
    			fileObj.println(text);
    			fileObj.close();
		}
		catch (Exception e)
        {
            ErrorReport.prtReport(e, "Error writing to " + overFilePath);
        }
	}
	private DBinfo info;
	private DBConn cmpDBC;  
	private CompilePanel cmpPanel;	// get all parameters from this
	private ArrayList <String> lines = new ArrayList <String> ();
	private DecimalFormat df = new DecimalFormat("#,###,###");
	private int nAsm=0, nMethod=0;
	ArrayList <String> assmStr = new  ArrayList <String> ();
	ArrayList <Integer> assmID = new  ArrayList <Integer> ();
	private ArrayList <String> methods = new ArrayList <String> ();
	
}